package cz.semjobKB.statistics;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import cz.semjobKB.linguistic.api.IWord;
import cz.semjobKB.triples.api.IMatch;
import cz.semjobKB.triples.api.ISentenceMatch;
import cz.semjobKB.triples.stat.StatCount;


public class MatchStatistics {

	public List<List<IWord>> sentPred = new ArrayList<List<IWord>>();
	public List<List<IWord>> sentFraze = new ArrayList<List<IWord>>();
	
	List<ISentenceMatch> matches;

	HashMap<String, Integer> mergeSub = new HashMap<String, Integer>();
	HashMap<String, Integer> mergePred = new HashMap<String, Integer>();
	HashMap<String, Integer> mergeObj = new HashMap<String, Integer>();
	HashMap<String, Integer> mergeSubPred = new HashMap<String, Integer>();

	StatCount counter = new StatCount();

	public MatchStatistics(List<ISentenceMatch> matchesS) {
		this.matches = matchesS;
	}

	public StatCount getCounter() {
		return counter;
	}
	
	public String mergeSortPrint() {
		
		counter.reset();
		counter.sentenceCount = matches.size();
		
		for (ISentenceMatch matchS : matches) {
			if (matchS.getSubjects().size() == 0) {
				counter.subjectMiss++;
			}
			if (matchS.getPredicates().size() == 0) {
				counter.predMiss++;
				sentFraze.add(matchS.getWords());
			} else {
				sentPred.add(matchS.getWords());
			}
			if (matchS.getSubPred().size() == 0) {
				counter.coupleMiss++;
			}
			if (matchS.getObjects().size() == 0) {
				counter.objectMiss++;
			}
			
			for(IMatch match : matchS.getSubjects()) {				
				counter.subjectsCount++;
				if(mergeSub.containsKey(match.getMatch())) {
					mergeSub.put(match.getMatch(), mergeSub.get(match.getMatch()) + 1);
				} else {
					mergeSub.put(match.getMatch(), 1);
				}		
			}
			
			for(IMatch match : matchS.getPredicates()) {
				counter.predCount++;
				if(mergePred.containsKey(match.getMatch())) {
					mergePred.put(match.getMatch(), mergePred.get(match.getMatch()) + 1);
				} else {
					mergePred.put(match.getMatch(), 1);
				}		
			}
			
			for(IMatch match : matchS.getObjects()) {
				counter.objectCount++;
				if(mergeObj.containsKey(match.getMatch())) {
					mergeObj.put(match.getMatch(), mergeObj.get(match.getMatch()) + 1);
				} else {
					mergeObj.put(match.getMatch(), 1);
				}		
			}
			
			for(IMatch match : matchS.getSubPred()) {
				counter.coupleCount++;
				if(mergeSubPred.containsKey(match.getMatch())) {
					mergeSubPred.put(match.getMatch(), mergeSubPred.get(match.getMatch()) + 1);
				} else {
					mergeSubPred.put(match.getMatch(), 1);
				}		
			}	
		}
		
		counter.subjectsUnique = mergeSub.size();
		counter.predUnique = mergePred.size();
		counter.objectUnique = mergeObj.size();
		counter.coupleUnique = mergeSubPred.size();
		
		counter = averageLength(counter);

		String result = String.format("%-30s\tOccurence\n","Sub");
		mergeSub = sortByValue(mergeSub);
		for (Entry<String, Integer> entry : mergeSub.entrySet()) {
			result += String.format("%-30s\t%s\n",entry.getKey(), entry.getValue().toString());
		}
		result += "\n-------------------------------------------------------\n";
		result += String.format("%-30s\tOccurence\n","Pred");
		mergePred = sortByValue(mergePred);
		for (Entry<String, Integer> entry : mergePred.entrySet()) {
			result += String.format("%-30s\t%s\n",entry.getKey(), entry.getValue().toString());
		}
		result += "\n-------------------------------------------------------\n";
		result += String.format("%-30s\tOccurence\n","Objects");
		mergeObj = sortByValue(mergeObj);
		for (Entry<String, Integer> entry : mergeObj.entrySet()) {
			result += String.format("%-30s\t%s\n",entry.getKey(), entry.getValue().toString());
		}
		result += "\n-------------------------------------------------------\n";
		result += String.format("%-30s\tOccurence\n","Triples");
		mergeSubPred = sortByValue(mergeSubPred);
		for (Entry<String, Integer> entry : mergeSubPred.entrySet()) {
			result += String.format("%-30s\t%s\n",entry.getKey(), entry.getValue().toString());
		}
		
		return result;
	}
	
	private StatCount averageLength(StatCount counter) {
		
		int wordsCountSent = 0;
		for (int index = 0 ; index < sentPred.size() ; index++) {
			wordsCountSent += sentPred.get(index).size();
		}
		counter.SentWords = wordsCountSent;
		counter.averageSentLength = wordsCountSent * 1.0 / sentPred.size();
		
		int wordsCountFraze = 0;
		for (int index = 0 ; index < sentFraze.size() ; index++) {
			wordsCountFraze += sentFraze.get(index).size();
		}
		counter.FrazeWords = wordsCountFraze;
		counter.averageFrazeLength = wordsCountFraze * 1.0 / sentFraze.size(); 
		return counter;
	}
	
	public String nicyPrint() {
		String result = "";		
		
		for (ISentenceMatch matchSentence : matches) {
			result += "SUB: [";
			if (matchSentence.getSubjects().size() > 0) {
				
			}
			for (IMatch match : matchSentence.getSubjects()) {
				result += match.getMatch() + ",";
			}
			
			result += "] PRED: [";
			for (IMatch match : matchSentence.getPredicates()) {
				result += match.getMatch() + ",";
			}
			
			result += "] OBJ: [";
			for (IMatch match : matchSentence.getObjects()) {
				result += match.getMatch() + ",";
			}
			result += "] TRIPLE: [";
			for (IMatch match : matchSentence.getSubPred()) {
				result += match.getMatch() + ",";
			}
			result += "] SENTENCE: [" + matchSentence.getSentenceText() + "]\n";
		}
				
		return result;
	}

	public static <K, V extends Comparable<? super V>> HashMap<K, V> sortByValue(Map<K, V> map) 
	{
	    @SuppressWarnings("unchecked")
	    Map.Entry<K,V>[] array = map.entrySet().toArray(new Map.Entry[map.size()]);

	    Arrays.sort(array, new Comparator<Map.Entry<K, V>>() 
	    {
	        public int compare(Map.Entry<K, V> e1, Map.Entry<K, V> e2) 
	        {
	            return e2.getValue().compareTo(e1.getValue());
	        }
	    });

	    HashMap<K, V> result = new LinkedHashMap<K, V>();
	    for (Map.Entry<K, V> entry : array)
	        result.put(entry.getKey(), entry.getValue());

	    return result;
	}
}
